دستورات ضروری Git که هر برنامه نویس باید بلد باشد

دستورات ضروری Git که هر برنامه نویس باید بلد باشد


اگر با توسعه نرم افزار سروکار داشته باشید، دیر یا زود با Git مواجه خواهید شد. فرقی نمی کند یک پروژه شخصی کوچک داشته باشید یا در یک تیم بزرگ روی یک محصول پیچیده کار کنید؛ Git یکی از مهم ترین ابزارهایی است که به شما کمک می کند تغییرات کد را مدیریت، تاریخچه پروژه را حفظ و همکاری با سایر اعضای تیم را ساده تر کنید.

مشکل از جایی شروع می شود که Git دستورات بسیار زیادی دارد و در نگاه اول ممکن است استفاده از آن ها گیج کننده باشد. اما برای کارهای روزمره، لازم نیست صدها دستور را حفظ کنید. اگر مجموعه ای از دستورات اصلی Git را به خوبی یاد بگیرید، بخش بزرگی از نیازهای واقعی شما در پروژه های نرم افزاری پوشش داده می شود.

Git چیست و چرا باید آن را یاد بگیریم؟

Git یک پروژه نرم افزار آزاد و متن باز است که در سال ۲۰۰۵ توسط لینوس توروالدز (Linus Torvalds)، خالق Linux، برای توسعه هسته لینوکس ایجاد شد و امروزه توسط جامعه توسعه دهندگان و با نگهداری اصلی Junio C Hamano توسعه می یابد.
نرم افزار Git تحت مجوز GNU GPL نسخه ۲ منتشر می شود و بنابراین خود نرم افزار برای استفاده، مطالعه، تغییر و توزیع آزاد است. از نظر حقوقی، علائم تجاری Git در اختیار Software Freedom Conservancy (SFC) است؛ SFC یک سازمان غیرانتفاعی آمریکایی است و حقوق علائم Git را از طرف پروژه Git مدیریت می کند. پروژه Git نرم افزار را رایگان عرضه می کند و منابع مالی آن عمدتاً از کمک ها و Donations تأمین می شود که SFC از طرف Git دریافت می کند و بخشی از آن برای هزینه هایی مانند رویدادهای توسعه دهندگان Git و بخشی برای هزینه های عملیاتی Conservancy اختصاص می یابد.
به عبارت دیگر Git یک سیستم کنترل نسخه توزیع شده (Distributed Version Control System) است. Git به شما اجازه می دهد تغییرات فایل های پروژه را در طول زمان ثبت کنید و در صورت نیاز به نسخه های قبلی برگردید.

برای مثال فرض کنید امروز در یک پروژه یک قابلیت جدید اضافه کرده اید و فردا متوجه شده اید این تغییر باعث ایجاد یک Bug شده است. اگر پروژه تحت کنترل Git باشد، می توانید تاریخچه تغییرات را بررسی کنید، ببینید چه چیزی تغییر کرده و حتی پروژه را به یک وضعیت قبلی برگردانید.

Git همچنین برای کار تیمی بسیار مهم است. هر توسعه دهنده می تواند روی Branch خودش کار کند و پس از تکمیل کار، تغییرات را با سایر اعضای تیم ادغام کند.

۱. git init — ایجاد Repository جدید

اگر یک پروژه جدید را روی کامپیوتر خود ایجاد کرده اید و می خواهید Git آن را مدیریت کند، از git init استفاده می کنید.

git init

اجرای این دستور باعث ایجاد پوشه مخفی .git در پوشه پروژه می شود. این پوشه اطلاعات مربوط به تاریخچه، Commitها، Branchها و تنظیمات Repository را نگهداری می کند.

مثال

فرض کنید پروژه ای به نام MyWebApp ساخته اید:

cd MyWebApp git init

از این لحظه Git این پوشه را به عنوان یک Repository محلی می شناسد.

git init معمولاً برای پروژه ای استفاده می شود که هنوز Repository محلی Git ندارد. اگر پروژه از قبل روی GitHub وجود داشته باشد، معمولاً به جای آن از git clone استفاده می کنیم.

۲. git config — تنظیم هویت توسعه دهنده

Git باید بداند Commitها متعلق به چه کسی هستند. برای تنظیم نام و ایمیل از git config استفاده می شود.

برای تنظیم عمومی در تمام پروژه ها:

git config --global user.name "hamedvahedi" git config --global user.email "you@example.com"

گزینه --global باعث می شود این تنظیمات برای Repositoryهای مختلف سیستم شما استفاده شوند.

برای تنظیم فقط در پروژه فعلی، --global را حذف کنید:

git config user.name "hamedvahedi"
git config user.email "you@example.com"

برای مشاهده تنظیمات:

git config --list

تنظیم صحیح هویت اهمیت زیادی دارد؛ زیرا نام و ایمیل شما در تاریخچه Commitها ثبت می شود.

۳. git clone — دریافت یک Repository موجود

اگر پروژه ای از قبل روی GitHub، GitLab یا یک Git Server دیگر وجود دارد، معمولاً لازم نیست آن را از ابتدا بسازید.

با git clone یک نسخه کامل از Repository را دریافت می کنید:

git clone https://github.com/hvahedi81/SampleRepo.git

این دستور علاوه بر فایل های پروژه، تاریخچه Commitها را نیز دریافت می کند و Repository محلی را به Repository راه دور متصل می کند.

بعد از Clone معمولاً می توانید وارد پوشه پروژه شوید:

cd SampleRepo

۴. git remote — مدیریت Repository راه دور

Repository محلی شما باید بداند کد را به کجا Push کند یا تغییرات را از کجا دریافت کند.

برای مشاهده Remoteهای موجود:

git remote -v

ممکن است خروجی مشابه زیر ببینید:

origin https://github.com/hvahedi81/SampleRepo.git (fetch)
origin https://github.com/hvahedi81/SampleRepo.git (push)

اگر Repository هنوز Remote ندارد، می توانید آن را اضافه کنید:

git remote add origin https://github.com/hvahedi81/SampleRepo.git

origin نام رایجی برای Remote اصلی است و یک نام قراردادی محسوب می شود.

۵. git status — بررسی وضعیت Repository

یکی از مهم ترین دستورهای Git همین دستور ساده است:

git status

قبل از بسیاری از عملیات Git بهتر است ابتدا git status را اجرا کنید.

این دستور به شما می گوید:

  • چه فایل هایی تغییر کرده اند؛
  • چه فایل هایی Stage شده اند؛
  • چه فایل هایی هنوز Untracked هستند؛
  • روی کدام Branch قرار دارید.

مثال

فرض کنید فایل Program.cs را تغییر داده اید:

git status

ممکن است Git اعلام کند:

modified: Program.cs

حالا می دانید که این فایل تغییر کرده ولی هنوز برای Commit آماده نشده است.

۶. git add — اضافه کردن تغییرات به Stage

ویرایش یک فایل به تنهایی به این معنی نیست که آن تغییر در Commit بعدی قرار می گیرد.

برای Stage کردن یک فایل:

git add Program.cs

برای Stage کردن همه تغییرات:

git add .

حالا اگر دوباره وضعیت را بررسی کنید:

git status

فایل موردنظر در قسمت Changes to be committed نمایش داده می شود.

مثال واقعی

فرض کنید دو فایل تغییر کرده اند:

Program.cs UserService.cs

اگر فقط فایل اول را می خواهید Commit کنید:

git add Program.cs

این قابلیت به شما اجازه می دهد فقط بخشی از تغییرات را برای Commit بعدی انتخاب کنید.

۷. git commit — ثبت یک Snapshot

Commit را می توان یک Snapshot از وضعیت پروژه در یک لحظه مشخص در نظر گرفت.

برای ایجاد Commit:

git commit -m "Add user authentication"

پیام Commit باید کوتاه اما معنادار باشد.

پیام هایی مثل:

fix update changes test

اطلاعات چندانی درباره تغییرات ارائه نمی کنند.

در مقابل:

Fix invalid login validation

یا:

Add user authentication service

برای بررسی تاریخچه پروژه بسیار مفیدتر هستند.

اگر فایل هایی که قبلاً توسط Git Track شده اند را تغییر داده اید، می توانید از -a استفاده کنید:

git commit -am "Fix login validation"

اما دقت کنید که git commit -a فایل های جدید و Untracked را Stage نمی کند.

۸. git commit --amend — اصلاح آخرین Commit

گاهی Commit را ایجاد کرده اید اما:

  • پیام Commit اشتباه است؛
  • یک فایل را فراموش کرده اید؛
  • یا می خواهید تغییرات کوچکی به آخرین Commit اضافه کنید.

در این شرایط:

git commit --amend

مثلاً:

git add LoginService.cs
git commit --amend

Git فایل Stage شده را به آخرین Commit اضافه می کند.

همچنین می توانید پیام آخرین Commit را تغییر دهید:

git commit --amend -m "Fix authentication service"

اگر Commit قبلی را قبلاً روی یک Branch مشترک Push کرده اید، استفاده از amend می تواند تاریخچه را تغییر دهد؛ بنابراین باید با احتیاط استفاده شود.

۹. git push — ارسال Commitها به Remote

تا اینجا Commitهای شما فقط روی کامپیوتر خودتان هستند.

برای ارسال آن ها به GitHub:

git push origin main

در این مثال:

  • origin نام Remote است.
  • main نام Branch است.

اگر Branch فعلی قبلاً به Remote متصل شده باشد، معمولاً می توانید فقط بنویسید:

git push

بعد از Push، سایر اعضای تیم می توانند تغییرات شما را از Repository راه دور دریافت کنند.

در پروژه های تیمی معمولاً توصیه نمی شود مستقیماً روی main کار کنید؛ بهتر است برای قابلیت یا Bug موردنظر یک Feature Branch ایجاد کنید و پس از Review آن را Merge کنید.

۱۰. git pull — دریافت و ادغام تغییرات

فرض کنید یکی از اعضای تیم تغییراتی را روی Remote Push کرده است. برای دریافت تغییرات و ادغام آن ها با Branch فعلی:

git pull

می توانید Remote و Branch را نیز مشخص کنید:

git pull origin main

به صورت مفهومی:

git pull = git fetch + git merge

یعنی Git ابتدا اطلاعات جدید Remote را دریافت می کند و سپس آن ها را با Branch فعلی ادغام می کند.

قبل از Pull کردن، بهتر است وضعیت تغییرات محلی خود را بررسی کنید:

git status

۱۱. git fetch — دریافت تغییرات بدون Merge

تفاوت مهم fetch و pull یکی از موضوعاتی است که هر توسعه دهنده Git باید آن را به خوبی درک کند.

با:

git fetch

تغییرات جدید Remote دریافت می شوند، اما Branch فعلی شما تغییر نمی کند.

برای مثال:

git fetch origin

حالا می توانید تغییرات Remote را بررسی کنید:

git log main..origin/main --oneline

این روش زمانی مفید است که می خواهید ابتدا تغییرات دیگران را بررسی کنید و بعد تصمیم بگیرید که آن ها را Merge یا Rebase کنید.

۱۲. git branch — ایجاد و مشاهده Branchها

Branch به شما اجازه می دهد روی یک مسیر توسعه مستقل کار کنید.

برای مشاهده Branchها:

git branch

برای ایجاد Branch جدید:

git branch new-feature

مثلاً:

git branch user-profile

در این حالت Branch ساخته می شود، اما هنوز به آن Branch منتقل نشده اید.

برای مشاهده Branchهای Remote نیز می توانید از:

git branch -a

استفاده کنید.

۱۳. git checkout — جابه جایی بین Branchها

برای رفتن به یک Branch دیگر:

git checkout user-profile

همچنین می توانید Branch جدید بسازید و همزمان به آن منتقل شوید:

git checkout -b user-profile

این دستور در واقع دو کار انجام می دهد:

Create Branch + Switch Branch

نکته مهم

git checkout دستور قدیمی و چندمنظوره ای است که علاوه بر Branchها برای بازیابی فایل ها و مشاهده Commitهای قدیمی نیز استفاده می شد.

در نسخه های جدید Git، دستورات git switch و git restore برای این وظایف تخصصی تر معرفی شده اند.

۱۴. git switch — روش جدیدتر برای تغییر Branch

برای جابه جایی بین Branchها می توان از:

git switch user-profile

استفاده کرد.

برای ساخت Branch جدید و ورود مستقیم به آن:

git switch -c user-profile

این روش از نظر مفهومی ساده تر از git checkout است؛ زیرا switch مشخصاً برای کار با Branchها طراحی شده است.

۱۵. git merge — ادغام Branchها

فرض کنید روی Branch زیر کار کرده اید:

user-profile

و کار شما تمام شده است.

ابتدا به main برگردید:

git switch main

سپس Branch را Merge کنید:

git merge user-profile

Git تغییرات user-profile را با Branch فعلی، یعنی main، ادغام می کند.

اگر تغییرات دو Branch با یکدیگر تداخل نداشته باشند، Git معمولاً Merge را به صورت خودکار انجام می دهد.

۱۶. Merge Conflict چیست؟

گاهی دو توسعه دهنده قسمت یکسانی از یک فایل را تغییر داده اند.

در این حالت Git نمی تواند به صورت خودکار تصمیم بگیرد کدام تغییر باید باقی بماند.

برای مثال ممکن است فایل دارای علامت هایی شبیه این شود:

<<<<<<< HEAD Console.WriteLine("Hello"); ======= Console.WriteLine("Hello World"); >>>>>>> user-profile

شما باید کد صحیح را انتخاب کرده و Markerهای Conflict را حذف کنید.

بعد:

git add Program.cs

و سپس:

git commit

فرآیند Merge تکمیل می شود.

۱۷. git rebase — مرتب کردن تاریخچه Commitها

git merge روش استاندارد و امنی برای ترکیب Branchهاست، اما در بعضی پروژه ها تیم ها ترجیح می دهند تاریخچه Git خطی تر و ساده تر باشد.

فرض کنید:

main
A---B---C

feature
\
D---E

در حالی که main جلو رفته است، می توانید روی Feature Branch خود اجرا کنید:

git rebase main

Git Commitهای Branch شما را روی آخرین Commit main مجدداً اعمال می کند.

نتیجه مفهومی:

A---B---C---D'---E'

تاریخچه ساده تر و خطی تر می شود.

هشدار مهم

روی Branch مشترکی که افراد دیگر بر اساس آن کار می کنند، بدون آگاهی از پیامدها Rebase نکنید.

rebase می تواند تاریخچه Commitها را بازنویسی کند.

۱۸. git log — مشاهده تاریخچه پروژه

برای مشاهده Commitهای پروژه:

git log

برای نمایش خلاصه تر:

git log --oneline

مثلاً:

8a2d91c Add authentication
71be22a Fix validation
34fa210 Add user model

برای نمایش تاریخچه همراه با ساختار Branchها:

git log --oneline --graph --all

این دستور برای بررسی نحوه شکل گیری تاریخچه پروژه بسیار مفید است.

۱۹. git diff — مشاهده تفاوت ها

قبل از Commit کردن تغییرات، بهتر است ببینید دقیقاً چه چیزی تغییر کرده است.

git diff

این دستور تفاوت بین فایل های تغییرکرده و وضعیت Commit شده را نشان می دهد.

اگر تغییرات را Stage کرده باشید، برای مشاهده تغییرات Stage شده:

git diff --staged

همچنین می توانید تفاوت دو Branch را بررسی کنید:

git diff main..user-profile

این قابلیت برای Code Review و بررسی تغییرات قبل از Merge بسیار کاربردی است.

۲۰. git stash — کنار گذاشتن موقت تغییرات

فرض کنید در حال توسعه یک قابلیت هستید، اما هنوز کارتان تمام نشده است.

ناگهان باید یک Bug مهم را روی Branch دیگری بررسی کنید.

نمی خواهید تغییرات فعلی را Commit کنید، اما نمی خواهید آن ها را هم از دست بدهید.

در این شرایط:

git stash

تغییرات فعلی موقتاً کنار گذاشته می شوند.

حالا می توانید Branch را تغییر دهید:

git switch main

پس از پایان کار، به Branch قبلی برگردید و تغییرات را بازیابی کنید:

git stash pop

یا:

git stash apply

تفاوت این دو مهم است:

git stash apply

تغییرات را بازیابی می کند ولی Stash را نگه می دارد.

در مقابل:

git stash pop

تغییرات را بازیابی کرده و در صورت موفقیت، آن Stash را حذف می کند.

۲۱. git reset — بازگرداندن وضعیت Branch

git reset یکی از قدرتمندترین و در عین حال حساس ترین دستورات Git است.

مثلاً:

git reset HEAD~1

Branch را یک Commit به عقب می برد.

اما رفتار reset به گزینه ای که استفاده می کنید بستگی دارد.

--soft

git reset --soft HEAD~1

Commit حذف می شود، اما تغییرات همچنان Stage شده باقی می مانند.

--mixed

git reset --mixed HEAD~1

Commit حذف می شود و تغییرات از Stage خارج می شوند، اما فایل ها و تغییرات شما باقی می مانند.

--hard

git reset --hard HEAD~1

Branch یک Commit به عقب برمی گردد و تغییرات موجود در Working Directory و Staging Area نیز حذف می شوند.

هشدار: استفاده از git reset --hard می تواند باعث از بین رفتن تغییرات Commit نشده شود. قبل از استفاده، مطمئن شوید چیزی را که نیاز دارید از دست نمی دهید.

۲۲. git revert — لغو امن یک Commit

اگر یک Commit را قبلاً روی یک Repository مشترک Push کرده اید و می خواهید اثر آن را برگردانید، معمولاً git revert انتخاب مناسب تری نسبت به reset است.

مثلاً:

git revert 8a2d91c

Git یک Commit جدید ایجاد می کند که اثر Commit موردنظر را معکوس می کند.

تفاوت اصلی:

reset تاریخچه را به عقب می برد
revert یک Commit جدید برای لغو تغییرات ایجاد می کند

بنابراین در Branchهای اشتراکی، revert معمولاً گزینه امن تری است.

۲۳. git cherry-pick — انتقال یک Commit خاص

گاهی نمی خواهید کل یک Branch را Merge کنید و فقط یک Commit خاص برای شما مفید است.

مثلاً فرض کنید یک Bug Fix در Branch دیگری وجود دارد:

feature-payment

و Hash آن Commit این است:

8a2d91c

می توانید در Branch فعلی اجرا کنید:

git cherry-pick 8a2d91c

Git تغییرات آن Commit را روی Branch فعلی اعمال می کند.

این قابلیت زمانی بسیار کاربردی است که فقط یک اصلاح مشخص را از یک Branch دیگر نیاز دارید.

توجه کنید که cherry-pick معمولاً Commit جدیدی با Hash متفاوت ایجاد می کند؛ بنابراین Commit جدید دقیقاً همان Commit قبلی از نظر شناسه نیست.

یک سناریوی واقعی: چرخه معمول کار با Git

حالا بیایید این دستورات را در یک سناریوی واقعی کنار هم قرار دهیم.

فرض کنید Repository پروژه را دریافت کرده ایم:

git clone https://github.com/hvahedi81/SampleRepo.git
cd SampleRepo

ابتدا وضعیت را بررسی می کنیم:

git status

یک Branch برای قابلیت جدید ایجاد می کنیم:

git switch -c user-profile

فایل های موردنظر را تغییر می دهیم و سپس:

git status

تغییرات را بررسی می کنیم:

git diff

فایل ها را Stage می کنیم:

git add .

و Commit می زنیم:

git commit -m "Add user profile"

سپس Branch را به Remote ارسال می کنیم:

git push -u origin user-profile

در یک پروژه تیمی، معمولاً در این مرحله Pull Request ایجاد می شود تا تغییرات بررسی و پس از Code Review با main ادغام شوند.

تفاوت دستورات مهم Git در یک نگاه

یکی از بهترین راه های یادگیری Git این است که تفاوت دستورات مشابه را بدانیم.

دستور کاربرد اصلی
git init ایجاد Repository محلی
git clone دریافت یک Repository موجود
git status مشاهده وضعیت Repository
git add انتقال تغییرات به Stage
git commit ثبت Snapshot
git push ارسال Commit به Remote
git fetch دریافت تغییرات Remote بدون Merge
git pull Fetch + Merge
git branch ایجاد/مشاهده Branch
git checkout تغییر Branch و عملیات قدیمی مرتبط
git switch تغییر Branch
git merge ادغام Branchها
git rebase بازنویسی مبنای Commitها برای تاریخچه خطی تر
git log مشاهده تاریخچه
git diff مقایسه تغییرات
git stash ذخیره موقت تغییرات
git reset جابه جایی HEAD و بازگردانی وضعیت
git revert ایجاد Commit معکوس
git cherry-pick اعمال یک Commit خاص روی Branch فعلی
git remote مدیریت Remoteها
git config تنظیم هویت و تنظیمات Git
git commit --amend اصلاح آخرین Commit

reset یا revert؛ کدام را انتخاب کنیم؟

این سوال یکی از مهم ترین سوالات Git است.

اگر Commit فقط روی سیستم خودتان است و می خواهید تاریخچه را اصلاح کنید:

git reset

می تواند گزینه مناسبی باشد.

اما اگر Commit را روی Branch مشترک Push کرده اید و سایر اعضای تیم ممکن است بر اساس آن کار کرده باشند:

git revert

معمولاً انتخاب امن تری است؛ چون تاریخچه موجود را حذف یا بازنویسی نمی کند.

merge یا rebase؟

هر دو برای ادغام تغییرات Branchها استفاده می شوند، اما فلسفه متفاوتی دارند.

merge تاریخچه واقعی Branchها را حفظ می کند و در صورت نیاز Merge Commit ایجاد می کند.

در مقابل rebase Commitها را روی مبنای جدید دوباره اعمال می کند و معمولاً تاریخچه خطی تری ایجاد می کند.

به صورت ساده:

Merge حفظ ساختار واقعی شاخه ها
Rebase تاریخچه خطی تر و مرتب تر

نکته مهم این است که Rebase تاریخچه را بازنویسی می کند؛ بنابراین روی Branchهای عمومی و مشترک باید با احتیاط استفاده شود.

pull یا fetch؟

اگر فقط می خواهید تغییرات Remote را دریافت کنید و قبل از ادغام آن ها را بررسی کنید:

git fetch

اگر می خواهید تغییرات Remote دریافت و با Branch فعلی ادغام شوند:

git pull

به همین دلیل fetch کنترل بیشتری به شما می دهد.

چند عادت خوب برای کار حرفه ای با Git

یادگیری Syntax دستورات Git کافی نیست. نحوه استفاده از آن ها نیز اهمیت دارد.

۱. قبل از تغییر Branch وضعیت را بررسی کنید

git status

۲. Commitهای کوچک و معنادار ایجاد کنید

به جای یک Commit بسیار بزرگ:

Update project

چند Commit مشخص ایجاد کنید:

Add user validation
Fix login error
Add password hashing

۳. قبل از Push تغییرات را بررسی کنید

git diff git status

۴. پیام Commit واضح بنویسید

پیام Commit باید به شما و اعضای تیم بگوید چه اتفاقی افتاده است.

۵. روی Branch مشترک با احتیاط Rebase کنید

قبل از Rebase مطمئن شوید که بازنویسی تاریخچه باعث مشکل برای سایر اعضای تیم نمی شود.

۶. reset --hard را بدون بررسی اجرا نکنید

این دستور می تواند تغییرات Commit نشده را از بین ببرد.

سید حامد واحدی سید حامد واحدی     4 مهر 1405